﻿using System;
using System.Linq;
using System.Xml.Linq;
using System.Collections.Generic;
using System.Device.Location;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Controls;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Controls.Maps;
using Microsoft.Phone.Controls.Maps.Platform;
using Microsoft.Phone.Shell;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using System.Collections;
using System.IO.IsolatedStorage;

namespace ThessalonikiGuide
{
    public partial class GPS : PhoneApplicationPage
    {
        readonly MapLayer _pinlayer = new MapLayer();
        readonly MapLayer _bloblayer = new MapLayer();
        readonly MapLayer _routelayer = new MapLayer();
        GeoCoordinateWatcher watcher;
        Pushpin pin;
        string goingto;
        GeoCoordinate destination;
        MapPolyline route;
        DateTime play;
        int position;
        public GPS()
        {
            InitializeComponent();
            play = DateTime.Now;
            map1.Mode = new AerialMode(true);
            map1.Children.Add(_pinlayer);
            map1.Children.Add(_bloblayer);
            map1.Children.Add(_routelayer);
        }
        protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
        {
            try
            {
                goingto = NavigationContext.QueryString["name"];
                watcher = new GeoCoordinateWatcher();
                watcher.PositionChanged += new EventHandler<System.Device.Location.GeoPositionChangedEventArgs<System.Device.Location.GeoCoordinate>>(watcher_PositionChanged);
                if (watcher.Permission == GeoPositionPermission.Granted)
                {
                    if (IsolatedStorageSettings.ApplicationSettings["canuselocation"] as bool? == true)
                    {
                        watcher.Start();
                    }
                    else
                    {
                        MessageBox.Show("Η εφαρμογή αυτή χρειάζεται πρόσβαση στην τοποθεσία σας για να λειτουργήσει σωστα.");
                        NavigationService.Navigate(new Uri("/Privacy.xaml?red=1", UriKind.Relative));
                    }
                }
                else
                {
                    MessageBox.Show("Για να λειτουργήσει η εφαρμογή πρέπει να ενεργοποιήσετε τις υπηρεσίες τοποθεσίας της συσκεβής σας απο τις Ρυθμήσεις (Settings) του κινητού σας επιλέγοντας 'location' και μετα 'On'.");
                }
                var coordinates = new List<string>(NavigationContext.QueryString["coordinates"].Split(new char[1] { ',' })).Select(y => Convert.ToDouble(y)).ToList();
                destination = new GeoCoordinate { Latitude = coordinates[1], Longitude = coordinates[0] };
                CentreDestination(null, null);
                DropPin();
                CentreMe(null, null);
                CalculateRoute();
                base.OnNavigatedTo(e);
            }
            catch { MessageBox.Show("Παρουσιάστηκε σφάλμα. Βεβαιωθείτε οτι έχετε ενεργοποιήσει τις υπηρεσίες τοποθεσίας του κινητού σας."); }
        }
        protected override void OnBackKeyPress(System.ComponentModel.CancelEventArgs e)
        {
         //   NavigationService.RemoveBackEntry();
            base.OnBackKeyPress(e);
        }
        public static double DegreeBearing(GeoCoordinate g1, GeoCoordinate g2)
        {
            return DegreeBearing(g1.Latitude, g1.Longitude, g2.Latitude, g2.Longitude);
        }
        public static double DegreeBearing(double lat1, double lon1, double lat2, double lon2)
        {
            var dLon = ToRad(lon2 - lon1);
            var dPhi = Math.Log(
                Math.Tan(ToRad(lat2) / 2 + Math.PI / 4) / Math.Tan(ToRad(lat1) / 2 + Math.PI / 4));
            if (Math.Abs(dLon) > Math.PI)
                dLon = dLon > 0 ? -(2 * Math.PI - dLon) : (2 * Math.PI + dLon);
            return ToBearing(Math.Atan2(dLon, dPhi));
        }

        public static double ToRad(double degrees)
        {
            return degrees * (Math.PI / 180);
        }

        public static double ToDegrees(double radians)
        {
            return radians * 180 / Math.PI;
        }

        public static double ToBearing(double radians)
        {
            // convert radians to degrees (as bearing: 0...360) 
            return (ToDegrees(radians) + 360) % 360;
        } 

        public static bool PointOnRoute(GeoCoordinate point, MapPolyline route)
        {
            for (var c = 0; c < route.Locations.Count() - 1; c++)
            {
                if (point.GetDistanceTo(route.Locations[c]) < 100)
                {
                    return true;
                }
                var diff = Math.Abs(DegreeBearing(route.Locations[c], point) - DegreeBearing(point, route.Locations[c + 1]));
                if( diff < 10)
                {
                    return true;
                }
            }
            return false;
        }
        void watcher_PositionChanged(object sender, System.Device.Location.GeoPositionChangedEventArgs<System.Device.Location.GeoCoordinate> e)
        {
            _bloblayer.Children.Clear();
            var location = ((GeoCoordinateWatcher)sender).Position.Location;
            _bloblayer.AddChild(new Ellipse() { Fill = new SolidColorBrush(Colors.Blue), Height = 40, Width = 40 }, location, PositionOrigin.Center);
            try
            {
                if (!PointOnRoute(location, route))
                {
                    _routelayer.Children.Clear();
                    CalculateRoute();
                }
                if (pin.Location.GetDistanceTo(location) < 100)
                {
                    _routelayer.Children.Clear();
                    _pinlayer.Children.Clear();
                    pin = null;
                    MessageBox.Show("Φτάσατε στο " + goingto + "!");
                }
                if (location.GetDistanceTo(new GeoCoordinate { Latitude = 40.626369, Longitude = 22.948428 }) <= 5000 && goingto == "Θεσσαλονίκη")
                {
                    MessageBox.Show("Καλωσήρθατε στην Θεσσαλονίκη!");
                    NavigationService.Navigate(new Uri("/MainPage.xaml", UriKind.Relative));
                }
                var turns = new List<GeoCoordinate>();
                var locs = route.Locations;
                turns.Add(locs[0]);
                turns.Add(locs.Last());
                locs.RemoveAt(0);
                locs.RemoveAt(locs.Count - 1);
                turns = locs.Where(y => y.GetDistanceTo(location) < 40 && y.GetDistanceTo(route.Locations[route.Locations.IndexOf(y) - 1]) < y.GetDistanceTo(route.Locations[route.Locations.IndexOf(y) + 1])).OrderBy(y => y.GetDistanceTo(location)).ToList();
                if (turns.Count() > 0 && (DateTime.Now.Subtract(play).TotalSeconds > 10 || route.Locations.IndexOf(turns.First()) != position))
                {
                    var sound = "start";
                    try
                    {
                        position = route.Locations.IndexOf(turns.First());
                        var prev = position - 1;
                        var next = position + 1;
                        var bearing1 = DegreeBearing(turns.First(), route.Locations[prev]);
                        var bearing2 = DegreeBearing(turns.First(), route.Locations[next]);
                        var angle = ToBearing(ToRad(bearing1 - bearing2)) - 180;
                        if (angle < -10)
                        {
                            sound = "right";
                        }
                        if (angle > 10)
                        {
                            sound = "left";
                        }
                    }
                    catch (ArgumentOutOfRangeException) { }
                    var stream = TitleContainer.OpenStream(sound + ".wav");
                    var effect = SoundEffect.FromStream(stream);
                    FrameworkDispatcher.Update();
                  //  effect.Play();
                    play = DateTime.Now;
                }
            }
            catch (NullReferenceException) { }
        }
        private void button1_Click(object sender, EventArgs e)
        {
            if (map1.Mode.ToString() == "Microsoft.Phone.Controls.Maps.AerialMode")
            {
                map1.Mode = new RoadMode();
                ((ApplicationBarIconButton)ApplicationBar.Buttons[0]).Text = "Κανονικό";
            }
            else
            {
                map1.Mode = new AerialMode(true);
                ((ApplicationBarIconButton)ApplicationBar.Buttons[0]).Text = "Μόνο Δρόμοι";
            }

        }

        private void DropPin()
        {
            AddPushpin(map1.Center, goingto);
        }
        private void AddPushpin(GeoCoordinate coordinate, String name)
        {
            _pinlayer.Children.Clear();
            pin = new Pushpin()
            {
                Location = coordinate
            };
            _pinlayer.AddChild(pin, coordinate, PositionOrigin.Center);
            _pinlayer.AddChild(new TextBlock()
            {
                Text = name
            }, coordinate, PositionOrigin.Center);
        }

        void CalculateRoute()
        {
            try
            {
                _routelayer.Children.Clear();
                var client = new RouteService.RouteServiceClient("BasicHttpBinding_IRouteService");
                client.CalculateRouteCompleted += new EventHandler<RouteService.CalculateRouteCompletedEventArgs>(client_CalculateRouteCompleted);
                var request = new RouteService.RouteRequest()
                {
                    Waypoints = new System.Collections.ObjectModel.ObservableCollection<RouteService.Waypoint>(){
                      new RouteService.Waypoint(){ Location = new Location(){ Latitude = watcher.Position.Location.Latitude, Longitude = watcher.Position.Location.Longitude}},
                      new RouteService.Waypoint(){ Location = new Location(){ Latitude = pin.Location.Latitude, Longitude = pin.Location.Longitude}}
                  },
                    Credentials = new Credentials()
                    {
                        ApplicationId = ((ApplicationIdCredentialsProvider)(map1.CredentialsProvider)).ApplicationId
                    },
                    Options = new RouteService.RouteOptions()
                    {
                        RoutePathType = RouteService.RoutePathType.Points
                    }
                };

                client.CalculateRouteAsync(request);
            }
            catch { }
        }

        void client_CalculateRouteCompleted(object sender, RouteService.CalculateRouteCompletedEventArgs e)
        {
            try
            {
                if (e.Error == null)
                {
                    var x = e.Result.Result;
                    var locs = new LocationCollection();
                    foreach (Location item in e.Result.Result.RoutePath.Points)
                    {
                        locs.Add(new GeoCoordinate(item.Latitude, item.Longitude));
                    }
                    route = new MapPolyline() { Locations = locs, Stroke = new SolidColorBrush(Colors.Green), StrokeThickness = 10 };
                    _routelayer.Children.Add(route);
                }
            }
            catch { }
        }

        private void CentreMe(object sender, EventArgs e)
        {
            try
            {
                var location = watcher.Position.Location;
                map1.Center = location;
                map1.ZoomLevel = 17;
            }
            catch { }
        }

        //private void Go_Click(object sender, RoutedEventArgs e)
        //{
        //    var client = new GeocodeService.GeocodeServiceClient("BasicHttpBinding_IGeocodeService");
        //    client.GeocodeCompleted += new EventHandler<GeocodeService.GeocodeCompletedEventArgs>(client_GeocodeCompleted);
        //    var request = new GeocodeService.GeocodeRequest()
        //    {
        //        Query = textBox1.Text,
        //        Credentials = new Credentials()
        //        {
        //            ApplicationId = ((ApplicationIdCredentialsProvider)(map1.CredentialsProvider)).ApplicationId
        //        }
        //    };
        //    client.GeocodeAsync(request);

        //}

        void client_GeocodeCompleted(object sender, GeocodeService.GeocodeCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                try
                {
                    map1.Center = e.Result.Results[0].Locations[0];
                    map1.ZoomLevel = 17;
                    return;
                }
                catch (ArgumentOutOfRangeException) { }
            }
            MessageBox.Show("No results.");
        }

        private void CentreDestination(object sender, EventArgs e)
        {
            map1.Center = destination;
            map1.ZoomLevel = 17;
        }

        private void ApplicationBarIconButton_Click(object sender, EventArgs e)
        {
            NavigationService.Navigate(new Uri("/Privacy.xaml", UriKind.Relative));
        }
    }
}